/*
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2004-2008 Intel Corporation. All Rights Reserved.
//
//
*/
#include "umc_defs.h"
#if defined (UMC_ENABLE_DV50_VIDEO_DECODER)

#include <ippi.h>
#include <ipps.h>
#include <ippvc.h>
#include "vm_event.h"
#include "vm_thread.h"
#include "vm_sys_info.h"
#include "umc_dv50_decoder.h"
#include "umc_dv50_internal.h"
#include "umc_dv_internal.h"
#include "umc_video_data.h"

#ifdef _OPENMP
#include "omp.h"
#endif

namespace UMC
{

DV50VideoDecoder::DV50VideoDecoder()
{

} // DV50VideoDecoder::DV50VideoDecoder()


DV50VideoDecoder::~DV50VideoDecoder()
{
    Close();

} // DV50VideoDecoder::~DV50VideoDecoder()

Status DV50VideoDecoder::Close()
{
    return DVVideoDecoder::Close();

} // Status DV50VideoDecoder::Close()

Status DV50VideoDecoder::Init(BaseCodecParams *lpInit)
{
    Status umcRes;

    // initialize parent class
    umcRes = DVVideoDecoder::Init(lpInit);
    if (UMC_OK != umcRes)
        return umcRes;

    // set working system
    m_nMaxNumberOfDIFSequences = ((HEIGHT_625 == m_nHeight) ? (24) : (20));

    if (SYSTEM_525 == m_nSystem) {
        m_nSourceFrameSize = 240000;
    } else  {
        m_nSourceFrameSize = 288000;
    }

    return UMC_OK;
} // Status DV50VideoDecoder::Init(BaseCodecParams *init_param)

void DV50VideoDecoder::CheckSetCorrectParams(int byteDSF)
{
    if ( byteDSF == 0x00)               //NTSC
    {
        m_ClipInfo.clip_info.height = 480;
        m_nHeight = 480;
        m_nSourceFrameSize = 2*120000;
        m_nMaxNumberOfDIFSequences = 2*10;
    }
    else if (byteDSF == 0x080)          //PAL
    {
        m_ClipInfo.clip_info.height = 576;
        m_nHeight = 576;
        m_nSourceFrameSize = 2*144000;
        m_nMaxNumberOfDIFSequences = 2*12;
    }
}

void DV50VideoDecoder::SelectStoreFunction(void* /*pSource*/)
{
    // set storing function
    switch (m_lSizeSubSampled)
    {
    case 2:
        StoreDVSegment = &DV50VideoDecoder::StoreDV50Segment_2s;
        break;

    case 4:
        StoreDVSegment = &DV50VideoDecoder::StoreDV50Segment_4s;
        break;

    case 8:
        StoreDVSegment = &DV50VideoDecoder::StoreDV50Segment_8s;
        break;

    default:
        StoreDVSegment = &DV50VideoDecoder::StoreDV50Segment;
        break;
    }
}

void DV50VideoDecoder::DecompressSegment(Ipp32u nThreadNum)
{
    Ipp32u i, k;
    Ipp32u b;
    Ipp32u i_start, i_stop;
    Ipp32u nTemp;
    Ipp8u *lpSrc;
    static Ipp32u BlockNumber[4] = {0, 2, 4, 5};

    // check error(s)
    if (nThreadNum >= m_nNumberOfThreads)
        return;

    // set working i & k
    nTemp = m_nMaxNumberOfDIFSequences;
    i_start = (nTemp * (nThreadNum)) / m_nNumberOfThreads;
    i_stop = (nTemp * (nThreadNum + 1)) / m_nNumberOfThreads;

    Ipp16u *lpsBlocks = m_ppShortBlocks[nThreadNum];

    if (false == m_bDCOnly)
    {
        Ipp32u BlParamBuffer[30];
        Ipp32u b_num, mb_num, block_type, block_quant_class, qno, bl_index, eob;
        Ipp16u *lpsTable;

        for (i = i_start;i < i_stop;i += 1)
        {
            for (k = 0;k < 27;k += 1)
            {
                // get source pointer
                lpSrc = m_lpSource + SIZE_OF_DV_SEGMENT * (i * 150 + 6 + (k / 3 + 1) + k * 5);

                //reset working block
                memset(lpsBlocks, 0, SIZE_OF_VIDEO_SEGMENT);

                // start video decompressing
                if(m_lSizeSubSampled == 2)
                    ippiHuffmanDecodeSegmentOnePass_DV_8u16s((Ipp8u *) lpSrc,
                                                             (Ipp32u *) _INTERNAL_DEZIGZAG_TABLE_0,
                                                             (Ipp32u *) m_pHuffTable,
                                                             (Ipp16s *) lpsBlocks,
                                                             (Ipp32u *) BlParamBuffer,
                                                              64);
                else
                    ippiHuffmanDecodeSegment_DV_8u16s( (Ipp8u *) lpSrc,
                                                       (Ipp32u *) _INTERNAL_DEZIGZAG_TABLE_0,
                                                       (Ipp32u *) m_pHuffTable,
                                                       (Ipp16s *) lpsBlocks,
                                                       (Ipp32u *) BlParamBuffer);

                // do dequantize and iDCT
                for (mb_num = 0;mb_num < 5;mb_num += 1)
                {
                    // get quantization number
                    qno = (BlParamBuffer[mb_num * 6] >> 16) & 0x0F;

                    // decompress each block
                    for (b = 0;b < 4;b += 1)
                    {
                        b_num = BlockNumber[b];
                        block_quant_class = (BlParamBuffer[mb_num * 6 + b_num] >> 4) & 0x03;
                        block_type = (BlParamBuffer[mb_num * 6 + b_num] >> 6) & 0x01;
                        bl_index = (BlParamBuffer[mb_num * 6 + b_num] >> 8) & 0xff;
                        eob = BlParamBuffer[mb_num * 6 + b_num]  & 0x01;

                                    // get beginning of arrays of dequantize tables
                        lpsTable =  m_lpADequantizeTable +
                                    // get needed array of tables, depending on block class
                                    (block_type * 64 * 14) +
                                    // get offset of needed table, depending on quantization class
                                    // & quantization number
                                    64 * m_lpADequantizeLineTable[qno + block_quant_class * 16];

                        // do inverse and adaptive dequantization
#ifdef FAST_DV
                        // fast but not exact quantization of DV
                        ippiQuantInv_DV_16s_C1I((Ipp16s *) (lpsBlocks + 64 * (b_num + 6 * mb_num)), (Ipp16s *) lpsTable);
#else
                        Ipp16s *wT;
                        if (0 == block_type)
                            wT = (Ipp16s *)TablW0_Scale_ZigZag;
                        else
                            wT = (Ipp16s *)TablW1_Scale_ZigZag;
                        // exact quantization of DV / DV50
                        ippiQuantWeightBlockInv_DV_16s_C1I(
                            (Ipp16s *) (lpsBlocks + 64 * (b_num + 6 * mb_num)),
                            (Ipp16s *) lpsTable,
                            wT );
#endif

                        // do iDCT
                        if (0 == block_type)
                        {
                            if(m_lSizeSubSampled == 2)
                                ippiDCT8x4x2To4x4Inv_DV_16s_C1I((Ipp16s *) (lpsBlocks + 64 * (b_num +6 * mb_num)));
                            else if ((eob == 0)||( 10 > bl_index))
                            {
                                ippiDCT8x8Inv_4x4_16s_C1I((Ipp16s *) (lpsBlocks + 64 * (b_num + 6 * mb_num)));
                            }
                            else
                                ippiDCT8x8Inv_16s_C1I((Ipp16s *) (lpsBlocks + 64 * (b_num + 6 * mb_num)));
                        }
                        else
                        {
                            if(m_lSizeSubSampled == 2)
                                ippiDCT8x4x2To4x4Inv_DV_16s_C1I((Ipp16s *) (lpsBlocks + 64 * (b_num +6 * mb_num)));
                            else
                                ippiDCT2x4x8Inv_16s_C1I((Ipp16s *) (lpsBlocks + 64 * (b_num +6 * mb_num)));
                        }
                    }
                }

                // store data to memory
                (this->*(StoreDVSegment))(i, k, nThreadNum);
            }
        }
    }
    else
    {
        for (i = i_start;i < i_stop;i += 1)
        {
            for (k = 0;k < 27;k += 1)
            {
                // get source pointer
                lpSrc = m_lpSource + SIZE_OF_DV_SEGMENT * (i * 150 + 6 + (k / 3 + 1) + k * 5);

                HuffmanDecodeSegment_DV_DC_only(lpSrc, lpsBlocks);

                // store data to memory
                (this->*StoreDVSegment)(i, k, nThreadNum);
            }
        }
    }

} // void DV50VideoDecoder::DecompressSegment(Ipp32u ThreadNum)

Status DV50VideoDecoder::GetPerformance(Ipp64f *perf)
{
    if (perf)
        *perf = 1.0;

    return UMC_OK;

} // Status DV50VideoDecoder::GetPerformance(Ipp64f *perf)

Status DV50VideoDecoder::Reset(void)
{
    if (!m_bInitSuccess)
        return UMC_ERR_NOT_INITIALIZED;
    else
        return UMC_OK;

} // Status DV50VideoDecoder::Reset(void)

} // end namespace UMC

#endif //(UMC_ENABLE_DV50_VIDEO_DECODER)
